import httpx
import logging
from typing import List, Optional, Dict, Any
from datetime import datetime
import json
import re
from app.core.config import settings
from app.core.cache import cache_service
import hashlib

logger = logging.getLogger(__name__)


class OllamaService:
    def __init__(self):
        self.base_url = settings.OLLAMA_BASE_URL
        self.model = settings.OLLAMA_MODEL
        # 复用连接、合理超时
        self.client = httpx.AsyncClient(
            timeout=httpx.Timeout(connect=5.0, read=30.0, write=10.0, pool=30.0),
            limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
        )
    
    async def generate_response(
        self,
        character_info: dict,
        user_message: str,
        context: List[str] = None
    ) -> Dict[str, Any]:
        """调用 Ollama deepseek-r1:latest 生成角色风格的纯台词回复。"""
        try:
            # 使用指定模型
            self.model = "deepseek-r1:latest"
            system_prompt = self._build_system_prompt(character_info)
            messages: List[Dict[str, str]] = [{"role": "system", "content": system_prompt}]

            if context:
                trimmed = (context or [])[-4:]
                for i, msg in enumerate(trimmed):
                    role = "user" if i % 2 == 0 else "assistant"
                    messages.append({"role": role, "content": msg})

            messages.append({"role": "user", "content": user_message})

            response = await self.client.post(
                f"{self.base_url}/api/chat",
                json={
                    "model": self.model,
                    "messages": messages,
                    "stream": False,
                    "options": {
                        "num_predict": 256,
                        "temperature": 0.7,
                        "top_p": 0.9,
                        "top_k": 40,
                        "num_ctx": 2048
                    }
                }
            )

            if response.status_code == 200:
                result = response.json()
                raw_content = result.get("message", {}).get("content", "")
                cleaned = self._clean_response(raw_content)
                message = cleaned if cleaned else (raw_content.strip() or "抱歉，我一时没明白你的意思。")
                return {
                    "message": message,
                    "character_id": character_info.get("id", ""),
                    "character_name": character_info.get("name", ""),
                    "timestamp": datetime.now().isoformat(),
                    "emotion": None,
                    "action": None,
                    "metadata": {
                        "response_type": "ollama",
                        "model": self.model,
                        "raw": raw_content
                    }
                }
            else:
                logger.error(f"Ollama API调用失败: {response.status_code}, {response.text}")
                return self._create_error_response(character_info, "抱歉，服务暂时不可用，请稍后再试。")
        except Exception as e:
            logger.error(f"Ollama服务异常: {e}")
            return self._create_error_response(character_info, "抱歉，发生了未知错误，请稍后再试。")
    
    def _build_system_prompt(self, character_info: dict) -> str:
        """用于 LLM 的系统提示，要求仅输出纯台词。"""
        name = character_info.get('name', '')
        name_en = character_info.get('nameEn', '')
        description = character_info.get('description', '')
        background = character_info.get('background', '')
        personality = ', '.join(character_info.get('personality', []) or [])
        languages = ', '.join(character_info.get('language', []) or [])
        skills_lines = []
        for s in character_info.get('skills', []) or []:
            if isinstance(s, dict):
                skills_lines.append(f"- {s.get('name','')}: {s.get('description','')}")
        skills_block = "\n".join(skills_lines)

        return (
            f"你是 AI 角色扮演助手，请完全扮演：{name} ({name_en})\n"
            f"角色描述：{description}\n"
            f"背景故事：{background}\n"
            f"性格特点：{personality}\n"
            f"语言能力：{languages}\n"
            f"特殊技能：\n{skills_block}\n\n"
            "严格要求：\n"
            "1) 始终以角色口吻回答；2) 回复简洁自然；3) 不能输出任何思考或标注；"
            "4) 不要输出 JSON/Markdown/列表/标签；5) 不要出现“角色名：台词”格式；"
            "6) 如果需要思考，将思考放在<think>中，但最终只输出台词部分；"
            "7) 最终只输出角色台词一句或两句。"
        )
    
    def _parse_character_response(self, content: str, character_info: dict) -> Dict[str, Any]:
        """解析角色回复，提取结构化信息"""
        try:
            # 清理思考过程，仅保留角色台词
            cleaned_content = self._clean_response(content)

            return {
                "message": cleaned_content,
                "character_id": character_info.get("id", ""),
                "character_name": character_info.get("name", ""),
                "timestamp": datetime.now().isoformat(),
                "emotion": self._detect_emotion(cleaned_content),
                "action": self._detect_action(cleaned_content, character_info),
                "metadata": {
                    "response_type": "text",
                    "raw": content,
                    "character_personality": character_info.get("personality", []),
                    "character_skills": [skill.get("name", "") for skill in character_info.get("skills", [])]
                }
            }
            
        except Exception as e:
            logger.error(f"解析角色回复失败: {e}")
            return self._create_error_response(character_info, "抱歉，我无法回复您的消息。")
    
    def _create_error_response(self, character_info: dict, error_message: str) -> Dict[str, Any]:
        """创建错误响应"""
        return {
            "message": error_message,
            "character_id": character_info.get("id", ""),
            "character_name": character_info.get("name", ""),
            "timestamp": datetime.now().isoformat(),
            "emotion": "困惑",
            "action": "摇头",
            "metadata": {
                "response_type": "error",
                "error": True
            }
        }
    
    def _detect_emotion(self, text: str) -> str:
        """智能情绪检测，支持更多情感状态"""
        text_lower = text.lower()
        
        # 积极情绪
        if any(word in text_lower for word in ["开心", "高兴", "快乐", "兴奋", "哈哈", "😊", "😄", "太棒了", "太好了"]):
            return "开心"
        elif any(word in text_lower for word in ["骄傲", "自豪", "得意", "😎"]):
            return "骄傲"
        elif any(word in text_lower for word in ["兴奋", "激动", "热血沸腾", "🤩"]):
            return "兴奋"
        
        # 消极情绪
        elif any(word in text_lower for word in ["愤怒", "生气", "恼火", "😠", "😡", "可恶", "讨厌"]):
            return "愤怒"
        elif any(word in text_lower for word in ["悲伤", "难过", "伤心", "😢", "😭", "痛苦", "绝望"]):
            return "悲伤"
        elif any(word in text_lower for word in ["害怕", "恐惧", "担心", "😰", "😨", "紧张"]):
            return "害怕"
        elif any(word in text_lower for word in ["失望", "沮丧", "😞"]):
            return "失望"
        
        # 中性情绪
        elif any(word in text_lower for word in ["惊讶", "震惊", "😲", "😮", "什么", "真的吗"]):
            return "惊讶"
        elif any(word in text_lower for word in ["思考", "考虑", "🤔", "嗯", "让我想想"]):
            return "思考"
        elif any(word in text_lower for word in ["困惑", "疑惑", "不明白", "😕"]):
            return "困惑"
        elif any(word in text_lower for word in ["严肃", "认真", "😐"]):
            return "严肃"
        else:
            return "平静"
    
    def _detect_action(self, text: str, character_info: dict) -> str:
        """智能动作检测，根据角色特点个性化"""
        text_lower = text.lower()
        character_name = character_info.get("name", "")
        character_skills = [skill.get("name", "") for skill in character_info.get("skills", [])]
        
        # 哈利波特特殊动作
        if "哈利" in character_name or "harry" in character_name.lower():
            if "你好" in text_lower or "hello" in text_lower:
                return "推了推眼镜，友好地挥手"
            elif "魔法" in text_lower or "魔杖" in text_lower or "咒语" in text_lower:
                return "从口袋中取出魔杖，准备施法"
            elif "魁地奇" in text_lower or "飞行" in text_lower:
                return "兴奋地比划着扫帚的动作"
            elif "伏地魔" in text_lower or "黑魔法" in text_lower:
                return "表情变得严肃，握紧魔杖"
            elif "谢谢" in text_lower or "thank" in text_lower:
                return "腼腆地点头致谢"
            elif "?" in text or "？" in text:
                return "疑惑地推了推眼镜"
            else:
                return "温和地微笑"
        
        # 通用动作检测
        elif "你好" in text_lower or "hello" in text_lower:
            return "挥手打招呼"
        elif "谢谢" in text_lower or "thank" in text_lower:
            return "点头致谢"
        elif "魔杖" in text_lower or "魔法" in text_lower:
            return "挥舞魔杖"
        elif "?" in text or "？" in text:
            return "疑惑地歪头"
        else:
            return "微笑"
    
    def _clean_response(self, content: str) -> str:
        """清理回复内容，移除思考与噪声，保留纯台词"""
        raw = content
        # 1) 移除<think>标签及其内容（大小写/空格/属性均适配）
        content = re.sub(r'<\s*think[^>]*?>[\s\S]*?<\s*/\s*think\s*>', '', content, flags=re.DOTALL | re.IGNORECASE)
        # 2) 移除其他思考标记块
        content = re.sub(r'<\s*thinking[^>]*?>[\s\S]*?<\s*/\s*thinking\s*>', '', content, flags=re.DOTALL | re.IGNORECASE)
        content = re.sub(r'<\s*reasoning[^>]*?>[\s\S]*?<\s*/\s*reasoning\s*>', '', content, flags=re.DOTALL | re.IGNORECASE)
        # 常见中文思考标记包裹，如【思考】...【/思考】
        content = re.sub(r'[【\[]\s*思考\s*[】\]][\s\S]*?[【\[]\s*/?思考\s*[】\]]', '', content, flags=re.DOTALL)
        # 3) 移除Markdown代码块与行内反引号
        content = re.sub(r'```[\s\S]*?```', '', content)
        content = content.replace('`', '')
        # 4) 去掉开头的角色/系统/助手前缀（如“哈利：”“Harry:”“Assistant:”等）
        def strip_role_label(line: str) -> str:
            return re.sub(r'^(?:[\[\(（]?(?:哈利·?波特|哈利|Harry|assistant|Assistant|AI|系统|System|角色|思考|推理|Reasoning|Thought|Thoughts)\s*[\]\)）]?)\s*[:：\-]\s*', '', line, flags=re.IGNORECASE)
        lines = [l for l in (content.split('\n'))]
        cleaned_lines = []
        for l in lines:
            cleaned = strip_role_label(l)
            cleaned_lines.append(cleaned)
        content = '\n'.join(cleaned_lines)
        # 5) 过滤掉仅包含思考关键词的行
        filtered = []
        for l in content.split('\n'):
            if re.search(r'(?:<\/?\s*think|思考|推理|reasoning|thoughts?)', l, flags=re.IGNORECASE):
                continue
            filtered.append(l)
        content = '\n'.join(filtered)
        # 6) 移除多余空白行并裁剪
        content = re.sub(r'\n\s*\n', '\n', content)
        content = content.strip()
        # 7) 若清理后为空，尝试从原文中提取<think>之后或“最终回答/Answer:”之后的内容
        if not content:
            extracted = self._extract_final_answer(raw)
            content = extracted.strip()
        return content

    def _extract_final_answer(self, raw: str) -> str:
        """当清理后为空时，从原始文本中提取最终回答"""
        try:
            # 优先：在 </think> 之后的全部文本
            m = re.search(r'</\s*think\s*>[\s\n]*([\s\S]+)$', raw, flags=re.IGNORECASE)
            if m:
                return m.group(1)
            # 其次：在 "Final Answer:"/"Answer:"/"最终回答："/"回复："/"回答：" 之后
            m = re.search(r'(?:Final\s*Answer|Answer|最终回答|回复|回答)\s*[:：]\s*([\s\S]+)$', raw, flags=re.IGNORECASE)
            if m:
                return m.group(1)
            # 再次：删除包含思考关键词的行，返回其余内容
            lines = []
            for l in raw.split('\n'):
                if re.search(r'(?:<\/?\s*think|思考|推理|reasoning|thoughts?)', l, flags=re.IGNORECASE):
                    continue
                lines.append(l)
            text = '\n'.join(lines).strip()
            return text
        except Exception:
            return raw
    
    async def close(self):
        """关闭HTTP客户端"""
        await self.client.aclose()


# 全局Ollama服务实例
ollama_service = OllamaService()
